package solutions;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;

import org.pi4.locutil.GeoPosition;
import org.pi4.locutil.io.TraceGenerator;
import org.pi4.locutil.trace.Parser;
import org.pi4.locutil.trace.TraceEntry;

import solutions.MainAlgorithmRunner.TraceEstimater;

public class RelateOnlineSamplesToMedianAccuracy {
	static final int offlineSize = 25;
	static final int onlineSize = 5;
	static final String wifiPositions = "data/MU.AP.positions";
	
	/**
	 * Get a number of samples from an offline trace, starting from the first
	 * sample in the trace.
	 */
	public static List<TraceEntry> getSamplesFromOnlineTrace(List<TraceEntry> offlineTrace,
			int numberOfSamples) {
		List<TraceEntry> outputSamples = new ArrayList<TraceEntry>();
		for (int i=0; i<numberOfSamples; i++) {
			outputSamples.add(offlineTrace.get(i));
		}
		return outputSamples;
	}
	
	/**
	 * Get the average distance between the real and estimated positions in a
	 * list of max. 10 samples.
	 */
	public static double getAverageDistanceBetweenPositions(TraceEstimater traceEstimater,
			List<TraceEntry> offlineTrace, int numberOfSamples) throws Exception {
		traceEstimater.buildRadioMap(offlineTrace);
		List<TraceEntry> onlineSamples = getSamplesFromOnlineTrace(offlineTrace, numberOfSamples);
		
		List<Double> distances = new ArrayList<Double>();
		for (TraceEntry traceEntry : onlineSamples) {
			GeoPosition realPosition = traceEntry.getGeoPosition();
			GeoPosition estimatedGeoPosition = traceEstimater.estimatePosition(traceEntry);
			distances.add(realPosition.distance(estimatedGeoPosition));
		}
		if (onlineSamples.size() == 1) {
			return distances.get(0);
		}
		if (onlineSamples.size() == 2) {
			return (distances.get(0) + distances.get(1)) / 2;
		}
		if (onlineSamples.size() == 3) {
			return (distances.get(0) + distances.get(1) + distances.get(2)) / 3;
		}
		if (onlineSamples.size() == 4) {
			return (distances.get(0) + distances.get(1) + distances.get(2)
				  + distances.get(3)) / 4;
		}
		if (onlineSamples.size() == 5) {
			return (distances.get(0) + distances.get(1) + distances.get(2)
				  + distances.get(3) + distances.get(4)) / 5;
		}
		if (onlineSamples.size() == 6) {
			return (distances.get(0) + distances.get(1) + distances.get(2)
				  + distances.get(3) + distances.get(4) + distances.get(5)) / 6;
		}
		if (onlineSamples.size() == 7) {
			return (distances.get(0) + distances.get(1) + distances.get(2)
				  + distances.get(3) + distances.get(4) + distances.get(5)
				  + distances.get(6)) / 7;
		}
		if (onlineSamples.size() == 8) {
			return (distances.get(0) + distances.get(1) + distances.get(2)
				  + distances.get(3) + distances.get(4) + distances.get(5)
				  + distances.get(6) + distances.get(7)) / 8;
		}
		if (onlineSamples.size() == 9) {
			return (distances.get(0) + distances.get(1) + distances.get(2)
				  + distances.get(3) + distances.get(4) + distances.get(5)
				  + distances.get(6) + distances.get(7) + distances.get(8)) / 9;
		}
		if (onlineSamples.size() == 10) {
			return (distances.get(0) + distances.get(1) + distances.get(2)
				  + distances.get(3) + distances.get(4) + distances.get(5)
				  + distances.get(6) + distances.get(7) + distances.get(8)
				  + distances.get(9)) / 10;
		}
		else {
			throw new Exception("Too many samples!");
		}
	}
	
	public static void main(String[] args) throws Exception {
		Parser offlineParser = new Parser(new File("data/MU.1.5meters.offline.trace"));
		Parser onlineParser = new Parser(new File("data/MU.1.5meters.online.trace"));
		List<Double> differences = new ArrayList<Double>(offlineSize);
		
		int k = 3; // 1-5

		TraceGenerator traceGenerator = new TraceGenerator(offlineParser, onlineParser, offlineSize, onlineSize);
		traceGenerator.generate();
		
		List<TraceEntry> apPositions = WifiPositionsParser.parser(wifiPositions);
		TraceEstimater fingerPrintingNNTraceEstimater = new FingerPrintingNN(traceGenerator, differences);
		TraceEstimater fingerPrintingKNNTraceEstimater = new FingerPrintingKNN(traceGenerator, differences, k);
		TraceEstimater modelNNTraceEstimater = new ModelNN(traceGenerator, differences, apPositions);
		TraceEstimater modelKNNTraceEstimater = new ModelKNN(traceGenerator, differences, k, apPositions);
		
		List<TraceEntry> offlineTrace = traceGenerator.getOffline();
		
		BufferedWriter writer = new BufferedWriter(new FileWriter(
				"RelateOnlineSamplesToMedianAccuracy.txt"));
		writer.write("#Number of samples\tFingerPrintingNN\tFingerPrintingKNN\tModelNN\tModelKNN\n");
		
		// Run all algorithms with 1 thru 10 samples respectively
		for (int i=0; i<10; i++) {
			System.out.println("Number of samples: " + (i+1));
			double fingerPrintingNN = getAverageDistanceBetweenPositions(fingerPrintingNNTraceEstimater, offlineTrace, i+1);
			double fingerPrintingKNN = getAverageDistanceBetweenPositions(fingerPrintingKNNTraceEstimater, offlineTrace, i+1);
			double modelNN = getAverageDistanceBetweenPositions(modelNNTraceEstimater, offlineTrace, i+1);
			double modelKNN = getAverageDistanceBetweenPositions(modelKNNTraceEstimater, offlineTrace, i+1);
			writer.write(i+1 + "\t" + fingerPrintingNN + "\t" + fingerPrintingKNN + "\t" + modelNN + "\t" + modelKNN + "\n");
			
			System.out.println("FingerPrintingNN: " + fingerPrintingNN);
			System.out.println("FingerPrintingKNN: " + fingerPrintingKNN);
			System.out.println("ModelNN: " + modelNN);
			System.out.println("ModelKNN: " + modelKNN);
			System.out.println("--------------------------");
		}
		writer.close();
		
		System.out.println("Done!");
	}
}
